/******************************************************************************
* 文件名称: portmacro.h
* 创建者の: 孔佳伟
* 个人主页: https://gitee.com/thin-wind/jump
* 修改记录: 
******************************************************************************/

#ifndef __PORTMACRO_H_
#define __PORTMACRO_H_

#include "stdint.h"
#include "stddef.h"
#include "rtosConfig.h"

// 开关中断响应要快，于是使用内联函数
#define portFORCE_INLINE                            inline __attribute__((always_inline))

// 定义任务函数
#define portTASK_FUNCTION(vFunction, pvParameters)  void vFunction(void *pvParameters)


/* 数据类型重定义 */
#define portCHAR        char
#define portFLOAT       float
#define portDOUBLE      double
#define portLONG        long
#define portSHORT       short
#define portSTACK_TYPE  uint32_t
#define portBASE_TYPE   long

typedef portSTACK_TYPE  StackType_t;
typedef long            BaseType_t;
typedef unsigned long   UBaseType_t;

// TickType_t 具体表示 16 位 还是 32 位，由 configUSE_16_BIT_TICKS 这个宏决定，
// 当该宏定义为 1 时，TickType_t 为 16 位，否则为 32 位。
// 该宏在 FreeRTOSConfig.h 中定义
#if (configUSE_16_BIT_TICKS == 1)
typedef uint16_t        TickType_t;
#define portMAX_DELAY   (TickType_t)0xffff
#else
typedef uint32_t        TickType_t;
#define portMAX_DELAY   (TickType_t)0xffffffffUL
#endif


// 查找最高优先级的就绪任务有两种方法，具体由 configUSE_PORT_OPTIMISED_TASK_SELECTION 这个宏控制
// 定义为 0 选择通用方法，定义为 1 选择根据处理器优化的方法 
#define configUSE_PORT_OPTIMISED_TASK_SELECTION     1


extern void vPortEnterCritical(void);
extern void vPortExitCritical(void);


// 中断控制状态寄存器：0xe000ed04 的 Bit 28 PENDSVSET: PendSV 悬起位
// 将 PendSV 的悬起位置 1，当没有其它中断运行的时候响应 PendSV 中断，
// 执行 PendSV 中断服务函数，在里面实现任务切换
#define portNVIC_INT_CTRL_REG       (*((volatile uint32_t *) 0xe000ed04))
#define portNVIC_PENDSVSET_BIT      (1UL << 28UL)

// 触发 PendSV 中断，进而切换任务
#define portYIELD()                                             \
        {                                                       \
            /* 触发 PendSV，产生上下文切换 */                     \
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;     \
            /* dsb 和 isb 完成数据同步隔离和指令同步隔离 */		   \
	        /* 保证之前存储器访问操作和指令都执行完 */			   \
            __asm("dsb");                                       \
            __asm("isb");                                       \
        }

// 不带返回值的关中断函数，不能嵌套
#define portDISABLE_INTERRUPTS()    vPortRaiseBASEPRI()
portFORCE_INLINE static void vPortRaiseBASEPRI(void)
{
    // 等同于 r0 = configMAX_SYSCALL_INTERRUPT_PRIORITY;
    __asm("mov r0, %0"::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY):"memory");
    __asm("msr basepri, r0");       // 关中断，进入临界段
    __asm("dsb");                   // 确保之前存储器访问已经完成
    __asm("isb");                   // 确保之前指令都已经执行完毕
}


// 带返回值的关中断函数，可以嵌套
#define portSET_INTERRUPT_MASK_FROM_ISR()  ulPortRaiseBASEPRI()
portFORCE_INLINE static uint32_t ulPortRaiseBASEPRI()
{
    uint32_t ulReturn;

    // 等同于 ulReturn = basepri;
    __asm("mrs %0, basepri" :"=r"(ulReturn)::"memory");

    // 等同于 r0 = configMAX_SYSCALL_INTERRUPT_PRIORITY;
    __asm("mov r0, %0"::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY):"memory");
    __asm("msr basepri, r0");       // 关中断，进入临界段
    __asm("dsb");                   // 确保之前存储器访问已经完成
    __asm("isb");                   // 确保之前指令都已经执行完毕
    return ulReturn;                // 返回原来 BASEPRI 的值
}


// 不带中断保护的开中断函数
// 直接将 BASEPRI 的值设置为 0，与 portDISABLE_INTERRUPTS() 成对使用
#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0)

// 带中断保护的开中断函数
// 将上一次关中断时保存的 BASEPRI 的值作为形参 ，与 portSET_INTERRUPT_MASK_FROM_ISR() 成对使用
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)

portFORCE_INLINE static void vPortSetBASEPRI(uint32_t ulBASEPRI)
{
    // basepri = ulBASEPRI;
    __asm("msr basepri, %0" ::"r"(ulBASEPRI):"memory");
}

// 进入临界段，不带中断保护版本，不能嵌套
#define portENTER_CRITICAL() vPortEnterCritical()

// 退出临界段，不带中断保护版本，不能嵌套
#define portEXIT_CRITICAL() vPortExitCritical()

// 将 uxReadyPriorities 变量中的 uxPriority 位置 1
#define portRECORD_READY_PRIORITY(uxPriority, uxReadyPriorities)\
    (uxReadyPriorities) |= (1UL << (uxPriority))

// 将 uxReadyPriorities 变量中的 uxPriority 位清 0
#define portRESET_READY_PRIORITY(uxPriority, uxReadyPriorities)\
    (uxReadyPriorities) &= ~(1UL << (uxPriority))

// 利用 CLZ 前导零指令计算从高位开始第一次出现 1 的位的前面的零的个数 
portFORCE_INLINE static uint8_t ucPortCountLeadingZeros(uint32_t ulBitmap)
    {
        uint8_t ucReturn;
        __asm volatile ( "clz %0, %1" : "=r"(ucReturn) : "r" (ulBitmap) : "memory");
        return ucReturn;
    }

// 找到最高优先级
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities )\
    uxTopPriority = (31UL - (uint32_t)ucPortCountLeadingZeros((uxReadyPriorities)))

#endif

